using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace Lyxite.CatKingdom
{
    class MouseSimulator
    {
        /// <summary>
        /// Round
        /// </summary>
        /// <param name="number"></param>
        /// <returns></returns>
        static int _R(double number)
        {
            return (int)Math.Round(number);
        }

        const int MINIMUN_SIZE = 2;

        readonly Brush _patchbrush = new SolidBrush(Color.FromArgb(50, Color.Blue));
        readonly Pen _borderpen = new Pen(Color.FromArgb(100, Color.Red));
        readonly Pen _gridpen = new Pen(Color.FromArgb(100, Color.Red));
        readonly Rectangle _screen = Screen.PrimaryScreen.Bounds;
        readonly RectangleF _screenF;

        Bitmap _oldbuffer;
        RectangleF _oldpatch;
        RectangleF _patch;
        bool _haspatch = false;

        public MouseSimulator()
        {
            _screenF = new RectangleF(_screen.X, _screen.Y, _screen.Width, _screen.Height);
            _gridpen.DashStyle = DashStyle.Dash;
        }

        public void Process(Actions actions, bool isUp)
        {
            switch (actions)
            {
                case Actions.LeftClick:
                case Actions.RightClick:
                case Actions.MiddleClick:
                    PerformClick(actions, isUp);
                    break;
                case Actions.DivideLeft:
                case Actions.DivideRight:
                case Actions.DivideUp:
                case Actions.DivideDown:
                case Actions.DivideUpLeft:
                case Actions.DivideUpRight:
                case Actions.DivideDownLeft:
                case Actions.DivideDownRight:
                    DivideTile(actions, isUp);
                    break;
                case Actions.MoveLeft:
                case Actions.MoveRight:
                case Actions.MoveUp:
                case Actions.MoveDown:
                case Actions.MoveUpLeft:
                case Actions.MoveUpRight:
                case Actions.MoveDownLeft:
                case Actions.MoveDownRight:
                    MoveTile(actions, isUp);
                    break;
                case Actions.Enlarge:
                case Actions.Shrink:
                    ResizeTile(actions, isUp);
                    break;
                case Actions.Clear:
                    ClearTile();
                    break;
            }
        }


        private void ClearTile()
        {
            _haspatch = false;
            RenderPatch();
            _oldbuffer = null;
        }

        private void PerformClick(Actions actions, bool isUp)
        {
            if (isUp)
            {
                switch (actions)
                {
                    case Actions.LeftClick:
                        WinAPI.mouse_event((uint)WinAPI.MouseEventFlags.LEFTUP, 0, 0, 0, 0);
                        break;
                    case Actions.RightClick:
                        WinAPI.mouse_event((uint)WinAPI.MouseEventFlags.RIGHTUP, 0, 0, 0, 0);
                        break;
                    case Actions.MiddleClick:
                        WinAPI.mouse_event((uint)WinAPI.MouseEventFlags.MIDDLEUP, 0, 0, 0, 0);
                        break;
                    default:
                        throw new ArgumentException();
                }
            }
            else
            {
                switch (actions)
                {
                    case Actions.LeftClick:
                        WinAPI.mouse_event((uint)WinAPI.MouseEventFlags.LEFTDOWN, 0, 0, 0, 0);
                        break;
                    case Actions.RightClick:
                        WinAPI.mouse_event((uint)WinAPI.MouseEventFlags.RIGHTDOWN, 0, 0, 0, 0);
                        break;
                    case Actions.MiddleClick:
                        WinAPI.mouse_event((uint)WinAPI.MouseEventFlags.MIDDLEDOWN, 0, 0, 0, 0);
                        break;
                    default:
                        throw new ArgumentException();
                }
            }
        }

        private bool ValidateRectangle(ref RectangleF newTile, ref RectangleF target)
        {
            if (newTile.Width < MINIMUN_SIZE || newTile.Height < MINIMUN_SIZE)
            {
                return false;
            }
            else if (_screenF.IntersectsWith(newTile) || _screenF.Contains(newTile))
            {
                if (newTile.Width > _screen.Width)
                {
                    newTile.X = 0;
                    newTile.Width = _screen.Width;
                }
                if (newTile.Height > _screen.Height)
                {
                    newTile.Y = 0;
                    newTile.Height = _screen.Height;
                }

                if (target.Equals(newTile))
                    return false;
                else
                {
                    target = newTile;
                    return true;
                }
            }
            else
            {
                return false;
            }
        }

        private void ResizeTile(Actions action, bool isUp)
        {
            if (isUp) return;

            if (!_haspatch)
            {
                _patch = _screen;
            }

            RectangleF newTile;

            switch (action)
            {
                case Actions.Shrink:
                    newTile = new RectangleF(_patch.X + _patch.Width / 4, _patch.Y + _patch.Height / 4, _patch.Width / 2, _patch.Height / 2);
                    break;
                case Actions.Enlarge:
                    newTile = new RectangleF(_patch.X - _patch.Width / 2, _patch.Y - _patch.Height / 2, _patch.Width * 2, _patch.Height * 2);
                    break;
                default:
                    throw new ArgumentException();
            }

            _haspatch = true;
            if (ValidateRectangle(ref newTile, ref _patch))
            {
                RenderPatch();
                UpdateCursor();
            }
        }

        private void MoveTile(Actions acition, bool isUp)
        {
            if (isUp)
                return;

            if (!_haspatch)
            {
                _patch = _screen;
            }

            RectangleF newTile;
            switch (acition)
            {
                case Actions.MoveDown:
                    newTile = new RectangleF(_patch.X, _patch.Y + _patch.Height / 2, _patch.Width, _patch.Height);
                    break;
                case Actions.MoveLeft:
                    newTile = new RectangleF(_patch.X - _patch.Width / 2, _patch.Y, _patch.Width, _patch.Height);
                    break;
                case Actions.MoveRight:
                    newTile = new RectangleF(_patch.X + _patch.Width / 2, _patch.Y, _patch.Width, _patch.Height);
                    break;
                case Actions.MoveUp:
                    newTile = new RectangleF(_patch.X, _patch.Y - _patch.Height / 2, _patch.Width, _patch.Height);
                    break;
                default:
                    throw new ArgumentException();
            }
            _haspatch = true;
            if (ValidateRectangle(ref newTile, ref _patch))
            {
                RenderPatch();
                UpdateCursor();
            }
        }

        private void DivideTile(Actions action, bool isUp)
        {
            if (isUp) return;

            if (!_haspatch)
            {
                _patch = _screen;
            }

            RectangleF newTile;
            switch (action)
            {
                case Actions.DivideUpLeft:
                    newTile = new RectangleF(_patch.X, _patch.Y, _patch.Width / 2, _patch.Height / 2);
                    break;
                case Actions.DivideUpRight:
                    newTile = new RectangleF(_patch.X + _patch.Width - _patch.Width / 2, _patch.Y, _patch.Width / 2, _patch.Height / 2);
                    break;
                case Actions.DivideDownLeft:
                    newTile = new RectangleF(_patch.X, _patch.Y + _patch.Height - _patch.Height / 2, _patch.Width / 2, _patch.Height / 2);
                    break;
                case Actions.DivideDownRight:
                    newTile = new RectangleF(_patch.X + _patch.Width - _patch.Width / 2, _patch.Y + _patch.Height - _patch.Height / 2, _patch.Width / 2, _patch.Height / 2);
                    break;
                default:
                    throw new ArgumentException();
            }

            _haspatch = true;
            if (ValidateRectangle(ref newTile, ref _patch))
            {
                RenderPatch();
                UpdateCursor();
            }
        }

        private void RenderPatch()
        {
            IntPtr dc = WinAPI.GetDC(IntPtr.Zero);
            Graphics g = Graphics.FromHdc(dc);

            // clear old tile
            if (_oldbuffer != null)
                g.DrawImage(_oldbuffer, _R(_oldpatch.X), _R(_oldpatch.Y), _R(_oldpatch.Width), _R(_oldpatch.Height));

            if (_haspatch)
            {
                // copy screen
                Rectangle tileInt = new Rectangle(_R(_patch.X), _R(_patch.Y), _R(_patch.Width), _R(_patch.Height));

                using (Bitmap buffer = new Bitmap(tileInt.Width, tileInt.Height))
                {
                    Graphics gbuf = Graphics.FromImage(buffer);
                    gbuf.CopyFromScreen(tileInt.X, tileInt.Y, 0, 0, tileInt.Size);

                    // make a copy
                    Bitmap buffer2 = new Bitmap(buffer);

                    // change the buffer
                    Rectangle zeroRect = new Rectangle(0, 0, tileInt.Width, tileInt.Height);
                    //gbuf.FillRectangle(new LinearGradientBrush(zeroRect,
                    //        Color.FromArgb(100, Color.Blue), Color.FromArgb(50, Color.Blue), 45
                    //    ), 
                    //    zeroRect);
                    gbuf.FillRectangle(_patchbrush, zeroRect);
                    gbuf.DrawRectangle(_borderpen, 0, 0, tileInt.Width - 1, tileInt.Height - 1);
                    gbuf.DrawLine(_gridpen, _patch.Width / 2, 0, _patch.Width / 2, _patch.Height);
                    gbuf.DrawLine(_gridpen, _patch.Width, _patch.Height / 2, 0, _patch.Height / 2);

                    // draw the buffer to screen
                    g.DrawImage(buffer, tileInt);

                    if (_oldbuffer != null)
                        _oldbuffer.Dispose();
                    _oldbuffer = buffer2;
                    _oldpatch = _patch;
                }
            }

            WinAPI.ReleaseDC(IntPtr.Zero, dc);
        }

        private void UpdateCursor()
        {
            if (_haspatch)
            {
                WinAPI.SetCursorPos(_R(_patch.X + _patch.Width / 2), _R(_patch.Y + _patch.Height / 2));
            }
        }
    }
}
